package analisador_PEWS;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import tools.IOArquivo;

import bean.MetodosPEWSXML;
import bean.MetodosServicos;
import bean.Operacoes;
import bean.Xaml;
import br.ufrn.plugin.pewsrt.preferences.PreferenceConstants;


public class AnalisadorPEWSXML {
	private DocumentBuilderFactory dbf;
	private DocumentBuilder db;
	private Document doc;	
	private AnalisadorWSDL analisadorWSDL;
	private StringBuilder conteudo; 		//Variavel responsavel pelo conteudo do arquivo XOML que irá ser gerado
	private Xaml xaml;
	private Element elem;					//Variavel que contem as informações do XPEWS 
	
	private Set<String> arquivosCS;
	private StringBuilder constantes;	
	private List<String> ifElseCondition;
	private List<String> whileCondition;	
	private List<MetodosPEWSXML> metodos; 	//Lista que conterá os metodos de serviços usados no documento XPEWS
	private List<MetodosServicos> listMetodosServico;
	private List<MetodosServicos> listMetodosPartialClass;
	private HashMap<String, String> aliasServicosEwsdl;		
	
	private String exeCod = new String();
	private int contParal;
	private int contSeq;
	private int delay;
	private Operacoes operacoes;
	
	public Element elementoRoot(){
		return doc.getDocumentElement();
	}

	public AnalisadorPEWSXML() {
		this.xaml  = new Xaml();
		this.conteudo = new StringBuilder();
		this.aliasServicosEwsdl = new HashMap<String, String>();
		
		this.arquivosCS 				= new HashSet<String>();
		this.analisadorWSDL 			= new AnalisadorWSDL();
		this.operacoes					= new Operacoes();

		this.ifElseCondition 			= new ArrayList<String>();
		this.whileCondition 			= new ArrayList<String>();

		this.listMetodosServico		 	= new ArrayList<MetodosServicos>();		
		this.listMetodosPartialClass 	= new ArrayList<MetodosServicos>();	
		this.metodos 					= new ArrayList<MetodosPEWSXML>();		
		
		this.constantes 				= new StringBuilder();
		
		this.contParal = 0;
		this.contSeq = 0;
		this.delay = 0;
		
		
		dbf = DocumentBuilderFactory.newInstance();
		try {
			db = dbf.newDocumentBuilder();
		} catch (ParserConfigurationException e) {
			System.out.println("DocumentBuilder nao pode ser criado.");
			e.printStackTrace();
		}
	}

	/**
	 * Metodo realiza a leitura do arquivo XML, usando parsers para a manipulação de seu conteudo 
	 * @param arquivoXML
	 * @throws SAXException
	 * @throws IOException
	 * @throws ParserConfigurationException 
	 * @throws XPathExpressionException 
	 */
	public void readerXML(String arquivoXML){
		try {
			this.doc = this.db.parse(arquivoXML);
		    this.elem = this.doc.getDocumentElement(); 		    
		    this.conteudo.append(xaml.cabecalho() + "\n");
		    xaml.setXname(arquivoXML.substring(0,arquivoXML.length()-4));	
		    
		    servicosUsados();
		    operations();
		    constantes();
		    pathexp();
		    //getInfoMetodos();

		    IOArquivo.salvarArquivo("demo.xoml",  this.conteudo.toString());
		} catch (SAXException e) {
			System.out.println("Erro no xml " + e.getMessage());
		} catch (IOException e) {
			System.out.println("Arquivo nao foi encontrado " + e.getMessage());
			//e.printStackTrace();
		} 		
	}
	
	public void getInfoMetodos(){
		System.out.println("\n Imprimindo os Metodos");
		for (MetodosServicos l : this.listMetodosServico) {
				System.out.println("Metodo: " + l.getMetodo() + "Alias: " + l.getAlias() +" Argumentos: " + l.getArgumentosTipos()  + " Retorno: " + l.getRetorno() + " Servico: " + l.getServico());			
		}
	}
	
//Serviços
	/**
	 * Metodo que verificar quais são os serviços usados no document XPEWS, guardando num hashMap
	 * O AliasServiço e o caminho do WSDL no hashmap this.aliasServicosEwsdl
	 */
	public void servicosUsados(){
		NodeList nl = this.elem.getElementsByTagName( "behaviour" );
		NamedNodeMap no = nl.item(0).getAttributes();
		for (int i=0; i<no.getLength();i++){
			if(no.item(i).getNodeName().startsWith("xmlns:"))
				this.aliasServicosEwsdl.put(no.item(i).getNodeName().substring(6), no.item(i).getNodeValue());
		}
	}
	
//Constantes
/**
 * Metodo que verificar a existencia de constantes 
 */
private void constantes() {
	NodeList constantesNode = this.elem.getElementsByTagName( "varDef" );
	if(constantesNode.getLength()>0){		
		for (int i=0; i<constantesNode.getLength();i++){
			NodeList varDef = constantesNode.item(i).getChildNodes();
			
			for(int j=0; j<varDef.getLength(); j++){	
				if(varDef.item(j).getNodeType() == Node.ELEMENT_NODE && varDef.item(j).getNodeType() != Node.TEXT_NODE){
					if(constantesNode.item(i).getAttributes().item(0).getNodeValue().equals("const")){
						System.out.println("ERRO: nome da variavel não pode ser usada");
					}
					System.out.println(constantesNode.item(i).getAttributes().item(0).getNodeValue() + " " + varDef.item(j).getAttributes().item(0).getNodeValue());
					this.constantes.append("\t \tint " + constantesNode.item(i).getAttributes().item(0).getNodeValue() + " = " + varDef.item(j).getAttributes().item(0).getNodeValue() + ";\n");
				}				
			}
		}	
	}
}
    
//Operations
	/**
	 * Responsavel por obter as informações pertencentes ao node operations do arquivo XPEWS
	 * A partir disso, quarda no addHash o aliasdo Metodo e o metodo que ele referencia presente no XPEWS
	 * Em seguida, é criado um objeto MetodoPEWSXML contendo o nome o metodo, seu alias 
	 * e qual servido ele é oriundo.
	 * 
	 */
	public void operations(){
	    NodeList nl = this.elem.getElementsByTagName( "operations" );
	    NodeList nod1 =  nl.item(0).getChildNodes();	    
	    
		for(int i=0; i<nod1.getLength();i++){
			Node nod = nod1.item(i);
			if(nod.getNodeType() == Node.ELEMENT_NODE && nod.getNodeType() != Node.TEXT_NODE){
				String aliasMetodo	= nod.getAttributes().getNamedItem("name").getNodeValue();
				String[] b =  nod.getAttributes().getNamedItem("refersTo").getNodeValue().toString().split(":");
				String aliasServico = b[0];				
				String metodoNome = b[1];
				MetodosPEWSXML alias = new MetodosPEWSXML(aliasServico, aliasMetodo, metodoNome);
				this.metodos.add(alias);			
				MetodosServicos metodo = new MetodosServicos(metodoNome);
				metodo.setAlias(aliasMetodo);
				metodo.setAliasServico(aliasServico);
				String url = aliasServicosEwsdl.get(aliasServico);
				metodo.setServico(this.analisadorWSDL.getServiceName(url));
				this.analisadorWSDL.funcoes(metodoNome);
				metodo.setArgumentos(this.analisadorWSDL.getArgumento(metodoNome));
				metodo.setTipodosArgumentos(this.analisadorWSDL.getTipoArgumento(metodoNome));
				metodo.setRetorno(this.analisadorWSDL.getTipoRetorno(metodoNome));
				this.listMetodosServico.add(metodo);
			}
		}
	}
	


//End Operations	
//path
	/**
	 * Responsavel por obter as informações pertencendo ao node pathExp do arquivo XPEWS
	 */
	public void pathexp(){
	    NodeList path = this.elem.getElementsByTagName( "pathExp" );
	    NodeList nodes =  path.item(0).getChildNodes();		    
	    this.operacoes.setMetodos(this.metodos);
	    getListadeNos(nodes);
	    this.conteudo.append("</SequentialWorkflowActivity>");
	}
	
	/**
	 * Recebe uma lista de nos, intera recursivamente até encontrar 
	 * @param lista
	 */
	public void getListadeNos(NodeList lista){
		for(int i=0; i<lista.getLength();i++){
			if(lista.item(i).getNodeType() == Node.ELEMENT_NODE && lista.item(i).getNodeType() != Node.TEXT_NODE){
				Node nod = lista.item(i);
				//System.out.println(nod.getNodeName());
		    	getNode(nod);
		    	if(nod.hasChildNodes() && !nod.getNodeName().equalsIgnoreCase("pred") && !nod.getNodeName().equalsIgnoreCase("condWhile")  && !nod.getNodeName().equalsIgnoreCase("While") && !nod.getNodeName().equalsIgnoreCase("operation") && !nod.getNodeName().equalsIgnoreCase("par")){
		    		getListadeNos(nod.getChildNodes());
		    	}
		    	fecharNode(nod);
			}

		}
	}
	
	/**
	 * Recebe uma lista de nos, intera recursivamente até encontrar 
	 * @param lista
	 */
	public void getListadeNos(Node no){
		getNode(no);
    	if(no.hasChildNodes() && !no.getNodeName().equalsIgnoreCase("pred") && !no.getNodeName().equalsIgnoreCase("operation") && !no.getNodeName().equalsIgnoreCase("par")){
    		getListadeNos(no.getChildNodes());
    	}
    	fecharNode(no);
	}
	
	/**
	 * Função que faz a tradução dos construtore PEWS para Workflow
	 * Verifica qual é tipo de construtor PEWS representa o Node passado por parametro
	 * E adiciona o referente tag do contrutor Workflow na variavel this.conteudo
	 * @param no
	 */
	public void getNode(Node no){
		//System.out.println(no.getNodeName());
		if(no.getNodeType() == Node.ELEMENT_NODE && no.getNodeType() != Node.TEXT_NODE){
			System.out.println(no.getNodeName());
			if(no.getNodeName().equalsIgnoreCase("seq")){				
				this.conteudo.append("<SequenceActivity x:Name=\"sequence" + this.contSeq + "\">"  + "\n");
				this.contSeq++;
			} else if(no.getNodeName().equalsIgnoreCase("par")){
				//System.out.println(no.getChildNodes().getLength());
				this.conteudo.append("<ParallelActivity x:Name=\"parallelActivity" + this.contParal + "\">"  + "\n");
				this.contParal++;
				paralelo(no);
			} else if(no.getNodeName().equalsIgnoreCase("pred")){
				predXOL(1);
				this.ifElseCondition.add(pred(no.getChildNodes()));
				predXOL(2);
			} else if(no.getNodeName().equalsIgnoreCase("while")){
				laco(no.getChildNodes());
			} else if(no.getNodeName().equalsIgnoreCase("wait")){
				wait(no);
			} else if(no.getNodeName().equalsIgnoreCase("operation")){
				atributosNos(no);
			}		
		}
	}
	
	/**
	 * Fecha a tag XML;
	 * @param no
	 */
	public void fecharNode(Node no){
		if(no.getNodeName().equalsIgnoreCase("seq")){
			//this.conteudo.append("\t<CodeActivity x:Name=\"finalizar\" ExecuteCode=\"finalizar_ExecuteCode\" />"  + "\n");
			this.conteudo.append("</SequenceActivity>"  + "\n");
		} else if(no.getNodeName().equalsIgnoreCase("par")){
			this.conteudo.append("</ParallelActivity>"  + "\n");
		}
	}
	
	/**
	 * O Node no, possue o valor do metodo de um servido do arquivo XPEWS,
	 * Essa função Captura os atributos do Node no: method = nome do metodo que irá ser chamado.
	 * No fim, é adicionado na variavel this. conteudo a tag XML que irá ser 
	 * responsavel pela chamada de um metodo no documento XOML que irá ser gerado.
	 * 
	 * @param no
	 */
	public void atributosNos(Node no){
		int tamanho  = no.getAttributes().getLength();
		String metodoNome = "";
		StringBuilder in = new StringBuilder();
		String out = "";		
		
		for(int i=0; i<tamanho;i++){			
			//System.out.println(no.getAttributes().item(i).getNodeValue());
			if(no.getAttributes().item(i).getNodeName().equalsIgnoreCase("name")){
				metodoNome = no.getAttributes().item(i).getNodeValue();
				this.conteudo.append("\t<CodeActivity x:Name=\""  + no.getAttributes().item(i).getNodeValue() + this.listMetodosPartialClass.size() +"\" ExecuteCode=\"" + metodoNome + this.listMetodosPartialClass.size() +"_ExecuteCode\" />"  + "\n");
				//this.partialClass.append(method + "Class." + method + "(" + ")");
				NodeList nl = no.getChildNodes();
				for(int j=0; j<nl.getLength();j++){
					if(nl.item(j).getNodeType() == Node.ELEMENT_NODE && nl.item(j).getNodeType() != Node.TEXT_NODE){
						if(nl.item(j).getNodeName().equalsIgnoreCase("in")){
							if(in.length() != 0){
								in.append(", " + nl.item(j).getAttributes().item(0).getNodeValue());
							} else {
								in.append(nl.item(j).getAttributes().item(0).getNodeValue());
							}
							
						} else if(nl.item(j).getNodeName().equalsIgnoreCase("out")){
							out = nl.item(j).getAttributes().item(0).getNodeValue();
						}
					}
				}
			}
		}
		
		MetodosServicos metodo = new MetodosServicos(metodoNome);
		metodo.setEntrada(in.toString());
		metodo.setSaida(out);
		this.listMetodosPartialClass.add(metodo);
		System.out.println("Metodo: "+ metodoNome  + " out: " + out + " in: " + in.toString());
		
	}
//End path
	
	private void predXOL(int i){
		if(i == 1){
			this.conteudo.append("\t <IfElseActivity x:Name=\"ifElseActivity"+ this.ifElseCondition.size() +"\">"  + "\n");
			this.conteudo.append("\t\t<IfElseBranchActivity x:Name=\"ifElseBranchActivity"+ this.ifElseCondition.size() +"\">"  + "\n");
			this.conteudo.append("\t\t\t<IfElseBranchActivity.Condition>"  + "\n");
			this.conteudo.append("\t\t\t\t<CodeCondition Condition=\"IfElseCondition"+ this.ifElseCondition.size() +"_ExecuteCode\" />"  + "\n");				
			this.conteudo.append("\t\t\t</IfElseBranchActivity.Condition>"  + "\n");
		} else {

			this.conteudo.append(this.exeCod);
			this.conteudo.append("\t\t</IfElseBranchActivity>"  + "\n");
			this.conteudo.append("\t </IfElseActivity>"  + "\n");
		}

	}
	
	private String pred(NodeList childNodes) {
		for(int i=0;i<childNodes.getLength();i++){	
			// verifica se é um node
			if(childNodes.item(i).getNodeType() == Node.ELEMENT_NODE && childNodes.item(i).getNodeType() != Node.TEXT_NODE){
				// verifica se tem filho
				System.out.println(childNodes.item(i).getNodeName());
				if(childNodes.item(i).hasChildNodes()){	
					
					if(childNodes.item(i).getNodeName().equalsIgnoreCase("and")){
						this.operacoes.and(childNodes.item(i).getChildNodes());
					} else if(childNodes.item(i).getNodeName().equalsIgnoreCase("or")){
						this.operacoes.or(childNodes.item(i).getChildNodes());
					} else if(childNodes.item(i).getNodeName().equalsIgnoreCase("operation")){
						//this.exeCod = "\t\t\t<CodeActivity x:Name=\"IfCondition\" ExecuteCode=\"IfCondition"+ this.nome+ this.ifElseCondition.size() +"_ExecuteCode\" />\n";
						this.atributosNos(childNodes.item(i));
						
					} else {
						this.operacoes.logicos(childNodes.item(i));
					}
				}
			}
		}
		//String retorno = this.condition.toString() + this.nome;
		
		String retorno = this.operacoes.getCondition();
		System.out.println(retorno);
		this.operacoes.clearCondition();
		
		return retorno;
	}
	
	private void laco(NodeList childNodes){
		this.conteudo.append("\t <WhileActivity x:Name=\"WhileActivity"+ this.whileCondition.size() +"\">"  + "\n");
		this.conteudo.append("\t\t<WhileActivity.Condition>"  + "\n");
		this.conteudo.append("\t\t\t<CodeCondition Condition=\"WhileActivity"+ this.whileCondition.size() +"_CodeCondition\" />"  + "\n");
		this.conteudo.append("\t\t</WhileActivity.Condition>"  + "\n");
		this.conteudo.append("<SequenceActivity x:Name=\"sequence" + this.contSeq + "\">"  + "\n");
		this.contSeq++;
		for(int i=0; i < childNodes.getLength(); i++){
			if(childNodes.item(i).getNodeType() == Node.ELEMENT_NODE && childNodes.item(i).getNodeType() != Node.TEXT_NODE){
				System.out.println(childNodes.item(i).getNodeName());
				if(childNodes.item(i).getNodeName().equalsIgnoreCase("condWhile")){
					NodeList nodelist = childNodes.item(i).getChildNodes();
					for(int j=0; j <nodelist.getLength(); j++){
						if(nodelist.item(j).getNodeType() == Node.ELEMENT_NODE && nodelist.item(j).getNodeType() != Node.TEXT_NODE){
							String nome =  pred(nodelist);
							//System.out.println(nome);
							this.whileCondition.add(nome);
									
						}
					}
				} else if(childNodes.item(i).getNodeName().equalsIgnoreCase("operation")){
					
					atributosNos(childNodes.item(i));
				}
				else {
					getListadeNos(childNodes.item(i));
				}
				
			}
		}
			
		//this.whileCondition.add(this.condition.toString());
		this.conteudo.append("</SequenceActivity>"  + "\n");
		this.conteudo.append("\t </WhileActivity>"  + "\n");
	}
	
	private void paralelo(Node no){
		NodeList nl = no.getChildNodes();
		for(int i=0; i< nl.getLength(); i++){
			if(nl.item(i).getNodeType() == Node.ELEMENT_NODE && nl.item(i).getNodeType() != Node.TEXT_NODE){
				System.out.println(nl.item(i).getNodeName());
				if(nl.item(i).getNodeName().equals("seq")){
					getListadeNos(nl.item(i));
				}
				else {
					this.conteudo.append("<SequenceActivity x:Name=\"sequence" + this.contSeq + "\">"  + "\n");
					this.contSeq++;
					getListadeNos(nl.item(i));
					this.conteudo.append("</SequenceActivity>"  + "\n");
				}
			}
		}
	}
	
	private void wait(Node no){
		//<DelayActivity TimeoutDuration="00:30:00" x:Name="delayActivity1" />
		this.conteudo.append("<DelayActivity TimeoutDuration=\"" + no.getAttributes().getNamedItem("time").getNodeValue() + "\" x:Name=\"delayActivity"+ this.delay +"\" />  \n");
		this.delay++;
		//System.out.println(no.getAttributes().getNamedItem("time").getNodeValue());
	}
	

	
	//METODOS AUXILIARES

		/**
		 * 
		 */
		public void gerarProxyWSDL(){
			String pasta = System.getProperty("user.dir") + "\\Workflow\\";
			this.arquivosCS = this.aliasServicosEwsdl.keySet();  
			try {  
		        for (String chave : this.arquivosCS) {  
		        	/*String comando = "C:\\Program Files\\Microsoft SDKs\\Windows\\v6.0A\\Bin\\wsdl.exe "
				    		+ "/out:" + pasta + chave + ".cs /n:FooBar "	+ this.aliasServicosEwsdl.get(chave);*/  
		        	String comando = PreferenceConstants.WSDL_PATH
				    		+ " /out:" + pasta + chave + ".cs /n:FooBar "	+ this.aliasServicosEwsdl.get(chave); 
		        	System.out.println("WSDL.exe " + comando); 
		        	Process processo = Runtime.getRuntime().exec(comando); 
		        }  
			}   
			catch (IOException e)   
			{  
			   System.out.println("Erro: "+e.getMessage()+"\n"+e.getCause());     
			}
		}

	public HashMap<String, String> getAliasServicosEwsdl() {
		return aliasServicosEwsdl;
	}

	public void setAliasServicosEwsdl(HashMap<String, String> aliasServicosEwsdl) {
		this.aliasServicosEwsdl = aliasServicosEwsdl;
	}

	public List<MetodosPEWSXML> getMetodos() {
		return metodos;
	}

	public void setMetodos(List<MetodosPEWSXML> metodos) {
		this.metodos = metodos;
	}

	public Set<String> getArquivosCS() {
		return arquivosCS;
	}

	public void setArquivosCS(Set<String> arquivosCS) {
		this.arquivosCS = arquivosCS;
	}
	
	
	public StringBuilder getConstantes() {
		return constantes;
	}

	public void setConstantes(StringBuilder constantes) {
		this.constantes = constantes;
	}
	

	public List<String> getIfElseCondition() {
		return ifElseCondition;
	}

	public void setIfElseCondition(List<String> ifElseCondition) {
		this.ifElseCondition = ifElseCondition;
	}
	
	

	public List<MetodosServicos> getListMetodosServico() {
		return listMetodosServico;
	}

	public void setListMetodosServico(List<MetodosServicos> listMetodosServico) {
		this.listMetodosServico = listMetodosServico;
	}

	
	
	public List<MetodosServicos> getListMetodosPartialClass() {
		return listMetodosPartialClass;
	}

	public void setListMetodosPartialClass(
		List<MetodosServicos> listMetodosPartialClass) {
		this.listMetodosPartialClass = listMetodosPartialClass;
	}
	
	

	public List<String> getWhileCondition() {
		return whileCondition;
	}

	public void setWhileCondition(List<String> whileCondition) {
		this.whileCondition = whileCondition;
	}

	/**
	 * Recebe o alias de um metodo e retorna o nome do metodo
	 * @param alias
	 * @return
	 */
	public String getMethodName(String alias){
		for (MetodosPEWSXML m : this.metodos) {
			if (m.getAlias().equalsIgnoreCase(alias));
				return m.getMetodo();
		}
		return null;
	}
	
}
